{% comment 'header' %}
# This file is part of opentaps Smart Energy Applications Suite (SEAS).

# opentaps Smart Energy Applications Suite (SEAS) is free software:
# you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# opentaps Smart Energy Applications Suite (SEAS) is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with opentaps Smart Energy Applications Suite (SEAS).
# If not, see <https://www.gnu.org/licenses/>.
{% endcomment %}

{% load static %}

<div id="meterproductionchart" v-cloak>
  <div class="card mb-3 border-nmd-0 m-n2 m-md-0">
    <div class="card-body p-md-3 p-0 mt-3 mt-md-0">
      {% if not no_title %}
      <h2>Meter Production</h2>
      {% endif %}
      <b-dropdown id="ddown2" :text="'For: ' + selectedRange" class="m-md-2" size="sm">
        <b-dropdown-item v-for="item in ranges" v-on:click="selectedRange = item">${item}</b-dropdown-item>
      </b-dropdown>
      <div class="row">
        <div class="col-12">
          <div class="row d-inline m-0 justify-content-center align-items-stretch">
            <apexchart
              ref="meter_production_line_chart"
              id="meter_production_line_chart"
              height=240
              type="line"
              :options="options"
              :series="series"></apexchart>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<style>
.apexcharts-toolbar-custom-icon {
  height: 24px;
  width: 30px;
}
</style>

<script>
(function() {
  {% load js_csrf_token from core_tags %}
  const CSRF_TOKEN = '{% js_csrf_token %}';

  // The global window.Apex variable below can be used to set common options for all charts on the page
  Apex = {
    dataLabels: {
      enabled: false
    },
    tooltip: {
      followCursor: false,
      theme: 'dark',
      x: {
        show: false
      },
      marker: {
        show: false
      },
    },
    grid: {
      clipMarkers: false
    },
  }
  var chartDegreeUnit = 'kWh'
  var myChart = new Vue({
    delimiters: ['${', '}'],
    el: '#meterproductionchart',
    components: {
      apexchart: VueApexCharts,
    },
    data: function() {
      return {
        ranges: ['10 years', '5 years', '2 years', '1 year', '6 months', '3 months', '1 month', '15 days', '7 days'{% if frequency != 'daily' %}, '24h'{% endif %}],
        selectedRange: '1 month',
        options: {
          chart: {
            id: 'meter-production-chart',
            toolbar: {
              tools: {
                download: false,
                selection: false,
                zoom: false,
                zoomin: false,
                zoomout: false,
                reset: false,
                pan: false,
                customIcons: [
                  {
                    icon: '<i class="fa fa-download"></i>',
                    title: 'Download',
                    class: '',
                    click: function(e) {
                      // Genereate CSV data first
                      let lines = [];
                      lines.push('Date/Time,Source,Value (' + chartDegreeUnit + ')');
                      for (let s = 0; s< myChart.series.length; s++) {
                        const chartData = myChart.series[s].data;
                        const chartName = myChart.series[s].name;
                        for (let i = 0; i < chartData.length; i++) {
                          let date = new Date(chartData[i].x).toLocaleString()
                          lines.push(['"', date, '"', ',', chartName, ',', chartData[i].y].join(''));
                        }
                      }

                      // Save data as csv file
                      saveTextAsCsvFile('Meter production.csv', lines.join('\n'));
                    }
                  },
                ]
              },
            },
          },
          legend: {
            show: true
          },
          xaxis: {
            type: 'datetime',
            labels: {},
          },
          yaxis: {
            labels: {
              formatter: function(value) {
                return value + ' ' + chartDegreeUnit
              }
            },
          },
          tooltip: {
            x: {
              format: 'dd MMM yyyy HH:mm'
            }
          }
        },
        series: [{
          name: "Value",
          data: [
            {
              x: new Date().getTime(),
              y: 0
            },
          ],
          rawData: [
            0,
          ]
        }],
      }
    },
    watch: {
      selectedRange: function(val, oldVal) {
        this.refresh();
      }
    },
    mounted() {
      this.init();
    },
    methods: {
      init() {
        this.refresh();
        eventHub.$on('meter_data_changed', this.refresh)
      },
      clear() {
        console.log('Clear charts.');
        // this.series.data.splice(0);
      },
      refresh() {
        this.fetchData();
      },
      getUrlArgs() {
        var args = '?range=' + this.selectedRange;
        {% if model_id %}
          args += '&model_id={{ model_id }}';
        {% endif %}
        return args;
      },
      fetchData() {
        url = '{% url 'core:meter_production_data_json' meter_id %}' + this.getUrlArgs();
        console.log('Fetching meter chart from ', url);
        axios.get(url).then(response => {
          if (response.data.uom) {
            console.log('Fetching meter chart got UOM ', response.data.uom);
            chartDegreeUnit = response.data.uom.unit;
          }
          console.log('Fetching meter chart got ', response.data);
          this.$refs['meter_production_line_chart'].updateOptions({markers: {size: 5, shape: 'circle'}, stroke: {width: 4}});
          if (response.data.values.length == 0) {
            this.series[0].data = [{y:0, x:new Date().getTime()}]
          } else {
            this.series = [];
            for (var k in response.data.values) {
              var l = response.data.values[k].length;
              if (l > 90) {
                this.$refs['meter_production_line_chart'].updateOptions({markers: {size: [0,3]}, stroke: {width: [1,2]}});
              } else if (l > 31) {
                this.$refs['meter_production_line_chart'].updateOptions({markers: {size: 3}, stroke: {width: 2}});
              }
              if (l > 90) {
                // do client side moving average ..
                // since we do not know if the serie is complete or regular
                // we have to look at the date (given by getTime() so in ms) and values and build a bucket
                // Since we can have many date ranges we scale to the current range if the number of points
                // is past a threshold 
                // for example here we look at the serie start date and end date and the number of points
                //
                var s_start = response.data.values[k][0];
                var s_end = response.data.values[k][l-1];
                var s_start_dt = parse_date(s_start.datetime).getTime();
                var s_end_dt = parse_date(s_end.datetime).getTime();

                var data = [];
                var m_avg = [];

                var bucket = {sum: 0, n: 0, start:0, end:0, target:0 }
                // scale the bucket to have roughly 50 points in moving average
                var bucket_size = (s_end_dt - s_start_dt) / 50.0;
                console.log('+ bucket_size: ', bucket_size);
                // translate the size into a human readable metric, we start at the bigger end
                // years
                var r = 365*86400000;
                var bs_hr = bucket_size/r;
                var bs_hr_str = 'years';
                var bs_hr_found = false;
                console.log('+ bucket_size: in '+bs_hr_str+' = ', bs_hr);
                if (bs_hr >= 1) {
                  // round it 
                  bs_hr = Math.round(bs_hr);
                  bucket_size = bs_hr * r;
                  bs_hr_found = true;
                } else {
                  // months (30 days)
                  r = 30*86400000;
                  bs_hr = bucket_size/r;
                  bs_hr_str = 'months';
                  console.log('+ bucket_size: in '+bs_hr_str+' = ', bs_hr);
                  if (bs_hr >= 1) {
                    // round it 
                    bs_hr = Math.round(bs_hr);
                    bucket_size = bs_hr * r;
                    bs_hr_found = true;
                  } else {
                    // days
                    r = 86400000;
                    bs_hr = bucket_size/r;
                    bs_hr_str = 'days';
                    console.log('+ bucket_size: in '+bs_hr_str+' = ', bs_hr);
                    if (bs_hr >= 1) {
                      // round it 
                      bs_hr = Math.round(bs_hr);
                      bucket_size = bs_hr * r;
                      bs_hr_found = true;
                    } else {
                      // hours
                      r = 3600000;
                      bs_hr = bucket_size/r;
                      bs_hr_str = 'hours';
                      console.log('+ bucket_size: in '+bs_hr_str+' = ', bs_hr);
                      if (bs_hr >= 1) {
                        // round it 
                        bs_hr = Math.round(bs_hr);
                        bucket_size = bs_hr * r;
                        bs_hr_found = true;
                      }
                    }
                  }
                }

                for (var i = 0; i < l; i++) {
                  // the point
                  var p = response.data.values[k][i];
                  var v = p.value;
                  var dt = parse_date(p.datetime).getTime();
                  // the direct serie
                  data.push({ x: dt, y: v });
                  // add to bucket:
                  if (bucket.n == 0) {
                    bucket.start = dt;
                    bucket.target = dt + bucket_size;
                    bucket.sum = 0;
                  }
                  bucket.sum += v;
                  bucket.n += 1;
                  bucket.end = dt;
                  // if we have enough
                  if (dt >= bucket.target) {
                    var m_avg_dt = (bucket.end + bucket.start) / 2.0;
                    var m_avg_v = bucket.sum / bucket.n;
                    m_avg.push({ x: m_avg_dt, y: m_avg_v });
                    // reset bucket
                    bucket.n = 0;
                  }
                }

                this.series.push({
                  name: k,
                  data: data
                })
                this.series.push({
                  name: k + ' m_avg' + (bs_hr_found ? ' ' + bs_hr + ' ' + bs_hr_str : ''),
                  data: m_avg
                })
              } else {
                // if not enough points just plot the series
                this.series.push({
                  name: k,
                  data: response.data.values[k].map(x => { return { y: x.value, x: parse_date(x.datetime).getTime() } } )
                })
              }
            }
          }
          });
      },
    }
  })
})();
</script>
